#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import redis
import traceback


class RedisDB(object):
    def __init__(self, conf, hostconf, portconf, dbconf, bfunc=None):
        self.db = None
        self.host = None
        self.port = None
        self.dbindex = None

        self.bfunc = bfunc

        self.hostkey, self.hostval = hostconf
        self.portkey, self.portval = portconf
        self.dbkey, self.dbval = dbconf

        self.conf = conf
        self.conf.setmonitor(self.cfg_changed)
        self.cfg_changed()

    def cfg_changed(self, cookie=None):
        host = self.conf.xget(self.hostkey, self.hostval)
        port = self.conf.xget(self.portkey, self.portval)
        dbindex = self.conf.xget(self.dbkey, self.dbval)

        if (
            not self.db
            or host != self.host
            or port != self.port
            or dbindex != self.dbindex
        ):
            if self.bfunc:
                self.bfunc(
                    "RedisDB: reload: host(%s=>%s), port(%d=>%d), db(%d=>%d)"
                    % (self.host, host, self.port, port, self.dbindex, dbindex)
                )
            self.db = redis.Redis(host=host, port=port, db=dbindex)
            if self.bfunc:
                self.bfunc("RedisDB: ", self.db)

            self.host = host
            self.port = port
            self.dbindex = dbindex


class RedisBatch(object):
    """
    rbat host port dbindex 'commanda arga argb ... argn' ' commandb ...'

    resList = RedisBatch(host, port, db)(["GET", "AAA"], ["HSET", "a", 333]])
    for res in resList:
        pass
    """

    def __init__(self, host, port, dbindex, bfunc=None):
        self.host = host
        self.port = port
        self.dbindex = dbindex

        self.bfunc = bfunc

    def __call__(self, cmds, contimode=False):
        self.db = redis.Redis(host=self.host, port=self.port, db=self.dbindex)

        results = []
        for cmd in cmds:
            try:
                if self.bfunc:
                    self.bfunc("RedisBatch: Before: ", *cmd)
                result = self.db.execute_command(*cmd)
                if self.bfunc:
                    self.bfunc("RedisBatch: Result: ", result)
                results.append(result)
            except:
                if self.bfunc:
                    self.bfunc("EXCEPT: ", traceback.format_exc())
                results.append(None)
                if contimode:
                    continue
                break

        return results


class RedisNotiCommand:
    """
    Common operation

    r = RedisNotiCommand()
    r.hmset(None, key, calldic)
    r.delete("event:msrem", nodekey)    # channel=event, message=msrem
    """

    def __init__(self, host=None, port=None, db=None, bfunc=None):
        self.host = host or "localhost"
        self.port = port or 6379
        self.db = db or 0
        self.bfunc = bfunc
        self.r = redis.StrictRedis(host=self.host, port=self.port, db=self.db)

    def bye(self):
        self.r.client_kill("%s:%d" % (self.host, self.port))

    def notify(self, chan, msg):
        self.r.publish(chan, msg)

    def __getattr__(self, name):
        def inner(msg, *args, **kwargs):
            def rediscall(name, *args, **kwargs):
                f = getattr(self.r, name)
                res = f(*args, **kwargs)

                if self.bfunc:
                    self.bfunc(
                        "RNC: name(%s), *args(%s), **kwargs(%s)"
                        % (name, str(args), str(kwargs))
                    )
                    self.bfunc("RNC: msg(%s)" % msg)
                    self.bfunc("RNC: res: ", res)

                if msg:
                    # msg = "<chan>:<servA>;<servB>;..."
                    segs = msg.split(":", 1)
                    self.r.publish(segs[0], segs[1])

                return res

            try:
                return rediscall(name, *args, **kwargs)
            except ConnectionError:
                if self.bfunc:
                    self.bfunc("ConnectionError: retry.")

                self.r = redis.StrictRedis(host=self.host, port=self.port)
                return rediscall(name, *args, **kwargs)
            except:
                if self.bfunc:
                    self.bfunc("EXCEPT: ", traceback.format_exc())

                return None

        return inner
